package tr3dtris.game.display;

import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.media.j3d.Alpha;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.swing.JPanel;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;
import tr3dtris.game.models.CellArray;
import tr3dtris.game.models.pieces.AbstractPiece;

/**
 *
 * @author Luciano
 */
public class Display3d extends EmptyDisplay {

    protected static final float BLOCKSIZE = 0.02f;
    protected static final float DEFAULTZ = 0f;
    protected Canvas3D canvas3d;
    protected JPanel sidePanel;
    protected SimpleUniverse simpleU;
    protected BranchGroup currentGrid;
    protected BranchGroup currentPiece;
    protected ViewingPlatform camera;
    protected Canvas3D canvas3dSmall;
    protected SimpleUniverse simpleUSmall;
    protected BranchGroup rootSmall;
    protected BranchGroup nextPiece;
    protected AbstractPiece oldNextPiece;
    

    public void refresh()
    {
        BranchGroup newGrid = this.buildGridBranchGroup();
        BranchGroup newPiece = this.buildCurrentPiece();
        this.simpleU.getLocale().replaceBranchGraph(currentGrid, newGrid);
        this.simpleU.getLocale().replaceBranchGraph(currentPiece, newPiece);
        this.currentGrid = newGrid;
        this.currentPiece = newPiece;
        
        if(this.game.getNextPiece() != this.oldNextPiece)
        {
            BranchGroup newNextPiece = this.buildNextPiece();
            this.simpleUSmall.getLocale().replaceBranchGraph(this.nextPiece, newNextPiece);
            this.nextPiece = newNextPiece;
            this.oldNextPiece = this.game.getNextPiece();
        }
    }

    public Display3d()
    {
        //creates the canvas3d
        this.canvas3d = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
        //creates the simple universe
        this.simpleU = new SimpleUniverse(this.canvas3d);

        //gets the camera from the SimpleUniverse
        this.camera = simpleU.getViewingPlatform();

        //configures camera attributes
        this.setCamera();

        

        //creates the default current Branch group and makes it detacheable
        this.currentGrid = new BranchGroup();
        this.currentGrid.setCapability(BranchGroup.ALLOW_DETACH);
        //this.simpleU.addBranchGraph(currentGrid);

        //creates the current piece branch group
        this.currentPiece = new BranchGroup();
        this.currentPiece.setCapability(BranchGroup.ALLOW_DETACH);

        //adds the current grid and the current piece to the simple universe
        this.simpleU.addBranchGraph(currentGrid);
        this.simpleU.addBranchGraph(currentPiece);

        this.canvas3dSmall = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
        this.simpleUSmall = new SimpleUniverse(this.canvas3dSmall);

        this.rootSmall = new BranchGroup();
        this.nextPiece = new BranchGroup();
        this.nextPiece.setCapability(BranchGroup.ALLOW_DETACH);

        this.oldNextPiece = null;
        
        this.configurePanels();
    }

    public void setCamera()
    {
        // This moves the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        camera.setNominalViewingTransform();

       Transform3D rotationY = new Transform3D();
       rotationY.rotY(Math.PI / 12);
       Transform3D rotationX = new Transform3D();
       rotationX.rotX(Math.PI / 12);
       Transform3D translation = new Transform3D();
       translation.setTranslation(new Vector3f(0.35f, -0.35f, 1.3f));
       translation.mul(rotationX);
       translation.mul(rotationY);
       camera.getViewPlatformTransform().setTransform(translation);
    }

    /**
     * Configure all the panels of the display
     */
    private void configurePanels()
    {
        this.setLayout(new BorderLayout());
        this.add(this.canvas3d, BorderLayout.CENTER);
        this.sidePanel = new JPanel();
        Dimension d = new Dimension(300, 768);
        this.sidePanel.setSize(d);
        this.sidePanel.setPreferredSize(d);
        this.add(sidePanel, BorderLayout.WEST);
        Dimension d2 = new Dimension(300,300);
        this.canvas3dSmall.setSize(d2);
        this.canvas3dSmall.setPreferredSize(d2);
        Color c = this.sidePanel.getBackground();
        float[] cc = c.getRGBColorComponents(new float[3]);
        Background back = new Background(cc[0], cc[1], cc[2]);
        BoundingSphere boundingSphere = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
        back.setApplicationBounds(boundingSphere);
        this.rootSmall.addChild(back);
        this.rootSmall.addChild(this.nextPiece);
        this.simpleUSmall.getViewingPlatform().setNominalViewingTransform();
        this.simpleUSmall.addBranchGraph(rootSmall);
        this.sidePanel.add(this.canvas3dSmall);
    }

    private BranchGroup buildGridBranchGroup()
    {
        BranchGroup _root = new BranchGroup();
        _root.setCapability(BranchGroup.ALLOW_DETACH);
        CellArray grid = this.game.getGrid();
        //creates blocks for the grid
        for(int x = 0; x < grid.getWidth(); x++)
            {
                if(!this.game.isPaused())
                    for(int y=0; y < grid.getHeight(); y++)
                    {
                        if(grid.hasBlockAt(x, y))
                        {
                            _root.addChild(this.createBlock(x * BLOCKSIZE * 2, y * BLOCKSIZE * 2, grid.getBlockAt(x, y).getType()-1));
                        }
                    }
            }
        _root.compile();
        return _root;
    }

    private BranchGroup buildCurrentPiece()
    {
        BranchGroup _root = new BranchGroup();
        _root.setCapability(BranchGroup.ALLOW_DETACH);
        //draw active piece
        if(game.getCurrentPiece() != null)
        {
            AbstractPiece cPiece = game.getCurrentPiece();
            if(!this.game.isPaused())
                for(int x=0; x < cPiece.getWidth(); x++)
                {
                    for (int y=0; y < cPiece.getHeight(); y++)
                    {
                        int posX = cPiece.getX();
                        int posY = cPiece.getY();
                        if(cPiece.getCells().hasBlockAt(x, y))
                            _root.addChild(this.createBlock((x + posX)*BLOCKSIZE * 2, (y + posY)*BLOCKSIZE * 2, cPiece.getTypeId()-1));
                    }
                }
        }
        _root.compile();
        return _root;
    }

    private BranchGroup buildNextPiece()
    {
        BranchGroup _root = new BranchGroup();
        _root.setCapability(BranchGroup.ALLOW_DETACH);

        BranchGroup group = new BranchGroup();

        TransformGroup tr = new TransformGroup(new Transform3D());
        tr.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        tr.addChild(group);

        // create time varying function to drive the animation
        Alpha rotationAlpha = new Alpha(-1, 4000);

        // Create a new Behavior object that performs the desired
        // operation on the specified transform object and add it into
        // the scene graph.
        RotationInterpolator rotator =
                new RotationInterpolator(rotationAlpha, tr);

        // a bounding sphere specifies a region a behavior is active
        BoundingSphere bounds = new BoundingSphere();
        rotator.setSchedulingBounds(bounds);

        tr.addChild(rotator);

        //draw active piece
        if(game.getNextPiece() != null)
        {
            AbstractPiece nPiece = game.getNextPiece();
            if(!this.game.isPaused())
                for(int x=0; x < nPiece.getWidth(); x++)
                {
                    for (int y=0; y < nPiece.getHeight(); y++)
                    {
                        int posX = nPiece.getX();
                        int posY = nPiece.getY();
                        if(nPiece.getCells().hasBlockAt(x, y))
                            group.addChild(this.createBlock((x + posX)*BLOCKSIZE * 2, (y + posY)*BLOCKSIZE * 2, nPiece.getTypeId()-1));
                    }
                }
        }
        _root.addChild(tr);

        _root.compile();
        return _root;
    }


    private TransformGroup createBlock(float x, float y, int type)
    {
        TransformGroup translationGroup = new TransformGroup();
        Transform3D translation = new Transform3D();
        translation.setTranslation(new Vector3f(x - BLOCKSIZE * this.game.getGrid().getWidth(), 
                                                -y + BLOCKSIZE * this.game.getGrid().getHeight(),
                                                DEFAULTZ));
        translationGroup.setTransform(translation);
        ColorCube obj = new ColorCube(BLOCKSIZE);
        
        translationGroup.addChild(obj);

        return translationGroup;
    }



}